package com.example.demo.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import com.example.demo.entity.pojo.PictureInfo;
import com.example.demo.service.PictureService;
import com.example.demo.service.UniversalService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit;

/**
 * @author Peng
 */
@Slf4j
@Service
@RequiredArgsConstructor
@SuppressWarnings("DuplicatedCode")
@Transactional(rollbackFor = Exception.class)
public class UniversalServiceImpl implements UniversalService {

    private final Searcher searcher;
    private final PictureService pictureService;

    /**
     * Read image info.
     * 提取照片里面的信息
     *
     * @param file the file 照片
     * @throws ImageProcessingException the image processing exception
     * @throws Exception                the exception
     */
    @Override
    public PictureInfo readImageInfo(MultipartFile file) throws ImageProcessingException, Exception {
        Metadata metadata = ImageMetadataReader.readMetadata(file.getInputStream());

        //照片全部信息
        for (Directory directory : metadata.getDirectories()) {
            for (Tag tag : directory.getTags()) {
                log.info("[{}] - {} = {}\n", directory.getName(), tag.getTagName(), tag.getDescription());
            }

            if (directory.hasErrors()) {
                for (String error : directory.getErrors()) {
                    log.info("ERROR: {}", error);
                }
            }
        }

        //定义经纬度信息和照片详情对象
        Double lat = null;
        Double lng = null;
        PictureInfo pictureInfo = new PictureInfo();

        //获得常用信息
        for (Directory directory : metadata.getDirectories()) {
            for (Tag tag : directory.getTags()) {
                String tagName = tag.getTagName();
                String desc = tag.getDescription();

                switch (tagName) {
                    case "Image Height":
                        pictureInfo.setHeight(desc);
                        break;
                    case "Image Width":
                        pictureInfo.setWidth(desc);
                        break;
                    case "Date/Time Original":
                        pictureInfo.setTime(desc);
                        break;
                    case "GPS Latitude":
                        pictureInfo.setLatitude(desc);
                        pictureInfo.setLatitudeFormat(pointToLatLong(desc));
                        lat = latLng2Decimal(desc);
                        break;
                    case "GPS Longitude":
                        pictureInfo.setLongitude(desc);
                        pictureInfo.setLongitudeFormat(pointToLatLong(desc));
                        lng = latLng2Decimal(desc);
                        break;
                    default:
                        break;
                }
            }
        }

        //经纬度转地址  ----------  经纬度转地主使用百度api
        if (null != lat && null != lng) {
            //有经纬度信息
            convertGpsToLocation(lat, lng, pictureInfo);
        }

        pictureService.save(pictureInfo);

        return pictureInfo;
    }

    @Override
    public String getIpStr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 本机访问
        if ("localhost".equalsIgnoreCase(ip) || "127.0.0.1".equalsIgnoreCase(ip) || "0:0:0:0:0:0:0:1".equalsIgnoreCase(ip)) {
            // 根据网卡取本机配置的IP
            InetAddress inet;
            try {
                inet = InetAddress.getLocalHost();
                ip = inet.getHostAddress();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (null != ip && ip.length() > 15) {
            if (ip.indexOf(",") > 15) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }

    @Override
    public String getAddressByIp(String ip) {
        //查询
        try {
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
            //μs -> 微妙 1s = 1000000μs
            log.info("region: {}, ioCount: {}, took: {} μs}", region, searcher.getIOCount(), cost);
            return region;
        } catch (Exception e) {
            log.error("failed to search({}): {}", ip, e);
        }

        // 备注：并发使用，用整个 xdb 数据缓存创建的查询对象可以安全的用于并发，也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。
        return null;
    }

    /**
     * 经纬度格式  转换为  度分秒格式 ,如果需要的话可以调用该方法进行转换
     *
     * @param point 坐标点
     */
    private String pointToLatLong(String point) {
        double du = Double.parseDouble(point.substring(0, point.indexOf("°")).trim());
        double fen = Double.parseDouble(point.substring(point.indexOf("°") + 1, point.indexOf("'")).trim());
        double miao = Double.parseDouble(point.substring(point.indexOf("'") + 1, point.indexOf("\"")).trim());
        double duStr = du + fen / 60 + miao / 60 / 60;
        return Double.toString(duStr);
    }

    /***
     * 经纬度坐标格式转换（* °转十进制格式）
     */
    private double latLng2Decimal(String gps) {
        String a = gps.split("°")[0].replace(" ", "");
        String b = gps.split("°")[1].split("'")[0].replace(" ", "");
        String c = gps.split("°")[1].split("'")[1].replace(" ", "").replace("\"", "");
        return Double.parseDouble(a) + Double.parseDouble(b) / 60 + Double.parseDouble(c) / 60 / 60;
    }


    /**
     * api_key：注册的百度api的key
     * coords：经纬度坐标
     * <p>
     * <a href="http://api.map.baidu.com/reverse_geocoding/v3/?ak="+api_key+"&output=json&coordtype=wgs84ll&location="+coords"><a/>
     * <p>
     * 经纬度转地址信息
     *
     * @param gpsLatitude  维度
     * @param gpsLongitude 精度
     */
    private void convertGpsToLocation(double gpsLatitude, double gpsLongitude, PictureInfo pictureInfo) {
        String apiKey = "YNxcSCAphFvuPD4LwcgWXwC3SEZZc7Ra";
        String flagOne = "result";
        String flagTwo = "addressComponent";

        String res;
        String url = "https://api.map.baidu.com/reverse_geocoding/v3/?ak=" + apiKey + "&output=json&coordtype=wgs84ll&location=" + (gpsLatitude + "," + gpsLongitude);
        log.info("图片转换 ->【url】" + url);

        res = HttpUtil.get(url);
        JSONObject object = JSONObject.parseObject(res);
        if (object.containsKey(flagOne)) {
            JSONObject result = object.getJSONObject("result");
            if (result.containsKey(flagTwo)) {
                JSONObject address = object.getJSONObject("result").getJSONObject("addressComponent");
                String addressInfo = address.get("country") + " " + address.get("province") + " " + address.get("city") + " " + address.get("district") + " "
                        + address.get("street") + " " + result.get("formatted_address") + " " + result.get("business");
                pictureInfo.setAddressInfo(addressInfo);
            }
        }
    }
}
